0.1 Overview

In this weeks assignment we will show how to create maps which contain geo spacial information. First, we will build a scatter map which shows information about various gas stations in the United States. After that, we will narrow down our view to the city of Philadelphia; analyzing crime data from the years of 2014-2015. Let’s see what kind of story we can extract by visualizing our data sets.

0.2 Gas Station Source Data

It’s important to describe any data set before working with it, so let’s begin by analyzing the raw gas station data. In the previous weeks assignements, we have loaded our data via having the csv in a local working directory. For this week, data was uploaded to my github. Here is how we can load the data via https.

gas_data <- read.csv("https://jmartin12.github.io/STAT553/data/gas_station_data.csv", header = TRUE)

Simple as that! First, let’s view the raw data in it’s table format, along with seeing how many rows we have.

  X site_row_id STATE      county         ADDRESS     CITY   ycoord    xcoord
1 1  1-3R8J-494    CA Los Angeles 37120 47TH ST E PALMDALE 34.55584 -118.0452
2 2  1-3R8J-362    WA    Franklin  1212 N 4TH AVE    PASCO 46.23890 -119.0950
                     SITE_DESCRIPTION service_or_fuel diesel
1 Los Angeles-Long Beach-Santa Ana CA            Fuel      Y
2         Kennewick-Pasco-Richland WA            Fuel      N
  twentyfour_hour_flag car_wash truckstop_flag description PUMP_TECH POC HIFCA
1                    Y        N              Y       URBAN         O   0     0
2                    N        N              N       URBAN         O   0     1
  ZIPnew POCAGE POCGAP ZIPPOC HFG  MSA dist.to.poc cate.poc.density
1  93552     NA     NA      0   1 4480   8.2275601       (-1e-06,1]
2  99301     NA     NA      1   0 6740   0.2788194       (-1e-06,1]
  cate.poc.age cate.poc.age.20 cate.poc.intensity cate.poc.intensity.tot
1     (15,140]        (15,140]            (5,Inf]                (8,Inf]
2     (15,140]        (15,140]            (5,Inf]                (8,Inf]
  MSA_POC MSA_POC.1
1       1         1
2       0         0
[1] 72798

While there are many columns in this data set, what’s important to notice for this weeks lesson is that we have two columns titled as ycoord and xcoord. These columns represent the coordinates in latitude and longitude, respectively. We will use both of these columns later when creating our map.

It’s also important to note the size of the data set; a mere 70K rows! If we plotted all of these on our map, it would be cluttered and difficult to interpret. Let’s reduce our data set size to simply 500 random rows. The code below demonstrates how we do this:

# Set the seed so you get the same rows I got.
set.seed(123)

# Get the 500 random rows
reduced_gas_data <- gas_data[sample(nrow(gas_data), 500), ]

# Print the row count
print(nrow(reduced_gas_data))
[1] 500

Success! We have reduced our data to 500 random rows.

0.3 Creating a Basic Geo Spacial Informative Scatter Map

For this week, we will focus on creating maps using the leaflet library. Please view the generated map, while reading an explanation of the code below.

# Create a leaflet map
gas_map <- leaflet() %>%
  addTiles() %>%
  setView(lng = -98.5795, lat = 39.8283, zoom = 3)

# Add markers for each gas station
for (i in 1:nrow(reduced_gas_data)) {
  gas_meta = paste('state: ',reduced_gas_data$STATE[i], '\n county: ', reduced_gas_data$county[i], '\n city: ' ,reduced_gas_data$CITY[i] ,'\n address: ', reduced_gas_data$ADDRESS[i])
  
  gas_map <- addMarkers(
    map = gas_map,
    lng = reduced_gas_data$xcoord[i],
    lat = reduced_gas_data$ycoord[i],
    label = htmltools::HTML(htmlEscape(gas_meta))
    )
}

gas_map

Walking through the code, we first start out with gas_map <- leaflet() %>% addTiles() %>% setView(lng = -98.5795, lat = 39.8283, zoom = 3).
The one thing that should be noted is that the default view was taken as the center point of the USA. Since our reduced data was randomly taken from the original 70K rows, using the mean of the lng and lat values will not give us a consistently good view, because the view is then subject to what gas stations we have randomly chosen. To circumvent this, the center point of the USA was used.

Aside to that, we then use a basic for loop control structure, in combination with the paste(...) function to add location metadata using the city, state, county, and address from our data set.

This is a primitive example of how the leaflet library can be used to graph geographical information. Let’s move onto a more advanced example so that we are able to extract more useful information and decipher a story therein.

0.4 Philly Crime Data

First, we will need a new data set that is hosted on my github.

philly_data <- read.csv("https://jmartin12.github.io/STAT553/data/PhillyCrimeSince2015.csv", header = TRUE)

An example few rows are given:

       dc_key                      race    sex    fatal           date
1 2.02422E+11      Black (Non-Hispanic) Female Nonfatal 3/3/2024 14:49
2 2.02426E+11 Hispanic (Black or White)   Male Nonfatal 3/1/2024 22:18
  has_court_case age   street_name block_number zip_code council_district
1             No  20 N COLORADO ST         2500    19132                5
2             No  58 N FRANKLIN ST         2600    19133                5
  police_district                       neighborhood house_district
1              22                  Sharswood-Stanton            181
2              26 Northern Liberties-West Kensington            197
  senate_district         school_catchment       lng      lat
1               3 Tanner G. Duckrey School -75.16060 39.99166
2               3 John F. Hartranft School -75.14468 39.99152

The total amount of rows are:

[1] 15520

Philly is definitely known for crime in certain areas. Let’s focus in on the year 2023. To accomplish this, we will have to perform some data transformations against the original date column. First, the following code parses out the year portion, and stores it in a vector.

years <- character(nrow(philly_data))

for (i in 1:nrow(philly_data)) {
  date_string <- philly_data$date[i]
  date_object <- strptime(date_string, "%m/%d/%Y %H:%M")
  years[i] <- format(date_object, "%Y")
}

head(years)
[1] "2024" "2024" "2024" "2024" "2024" "2024"

Great! Now we have all the years. Let’s add it as a new column to our original philly crime dataset.

philly_data$year <- years
head(philly_data, 2)
       dc_key                      race    sex    fatal           date
1 2.02422E+11      Black (Non-Hispanic) Female Nonfatal 3/3/2024 14:49
2 2.02426E+11 Hispanic (Black or White)   Male Nonfatal 3/1/2024 22:18
  has_court_case age   street_name block_number zip_code council_district
1             No  20 N COLORADO ST         2500    19132                5
2             No  58 N FRANKLIN ST         2600    19133                5
  police_district                       neighborhood house_district
1              22                  Sharswood-Stanton            181
2              26 Northern Liberties-West Kensington            197
  senate_district         school_catchment       lng      lat year
1               3 Tanner G. Duckrey School -75.16060 39.99166 2024
2               3 John F. Hartranft School -75.14468 39.99152 2024

Easy as that! We can see the year column is now added to the data set. To narrow down a subset of 2023 data only, we can reduce our data set by filtering on the newly added year column.

philly_data_2023 <- subset(philly_data, year == 2023)
head(philly_data_2023$date, 5)
[1] "12/31/2023 4:57"  "12/31/2023 3:35"  "12/31/2023 3:13"  "12/30/2023 22:56"
[5] "12/30/2023 11:32"

Only the date column is shown in the above example – the remaining columns are still stored in memory. We can clearly see there are only dates in 2023.

0.5 Philly Crimes - Mapped

Now that we have our 2023 data, let’s go ahead and use leafly to map our data. The code below adds two additional columns to the dataset so we can easily determine what colors to use, and in addition generating the label information.

From there, a title along with a legend is added to the graph.

colors <- character(nrow(philly_data_2023))
labels <- character(nrow(philly_data_2023))
 
for (i in 1:nrow(philly_data_2023)) {
  # Handle the colors
  if (philly_data_2023$fatal[i] == 'Fatal') {
    colors[i] <- '#000000'
  }
  else {
    colors[i] <- '#CC79A7'    
  }
  
  # Handle the info to display in the label
  labels[i] <- paste('Gender: ', philly_data_2023$sex[i], 'Neighborhood: ', philly_data_2023$neighborhood[i], 'Block Number: ', philly_data_2023$block_number[i])
}

philly_data_2023$crime_type_color <- colors
philly_data_2023$label <- labels

## Map title
title <- tags$div( HTML('<font color = "darkred" size =4><b>Philly Fatal and Non-Fatal Crimes 2023</b></font>')
)

# Create a Leaflet map
m <- leaflet(philly_data_2023) %>%
  addTiles() %>%
  setView(lat = 40, lng = -75.1652, zoom = 11) %>%
  addCircleMarkers(
    lng = ~lng,
    lat = ~lat,
    color = ~crime_type_color,
    radius = 3,
    label = ~label,
    labelOptions = labelOptions(noHide = FALSE, textOnly = FALSE)) %>%
  addControl(title, position = "topright") %>%
  addLegend(position = "bottomleft", 
            colors = c("#CC79A7", "#000000"),
            labels= c("Non Fatal", "Fatal"),
            title= "Crime Type",
            opacity = 0.8)

m



0.6 Narration

It is particularly interesting to see the distribution of crimes across the city of Philly. There are a few hotspots of fatal crimes. The primary hotspot being the southwest region of the city, followed by a smaller hotspot just north of the city.

The dataset itself can tell us more with different graphs. Let’s view the % of crimes committed by gender.

# Count the number of crimes per gender
crimes_per_gender <- philly_data_2023 %>%
  group_by(sex) %>%
  summarise(crime_count = n())

# Calculate the percentage of each gender
crimes_per_gender$percentage <- (crimes_per_gender$crime_count / sum(crimes_per_gender$crime_count)) * 100

# Create a pie chart
p3 <- plot_ly(crimes_per_gender, labels = ~sex, values = ~percentage, type = 'pie') %>%
  layout(title = "% of Crimes Committed by Males vs. Females")

# Print the plot
p3

It seems that males commit most of the crimes in the city of Philadelphia. Something else of interest could be to analyze which school districts have the highest crime count. In particular, parents could find this data to be of use when determining what school district to send their kids to.

  # Count the number of crimes per school district
  crimes_per_district <- philly_data_2023 %>%
    group_by(school_catchment) %>%
    summarise(crime_count = n()) %>%
    top_n(10, crime_count)

  
  # Create a bar graph
  p <- plot_ly(crimes_per_district, y = ~school_catchment, x = ~crime_count, type = 'bar') %>%
    layout(title = "Number of Crimes per School District in Philadelphia (2023)",
           xaxis = list(title = "School District"),
           yaxis = list((title = "Number of Crimes")))
  
  # Print the plot
  p

These ten schools are the schools with the highest number of reported crimes, indicating a potentially higher crime rate in these areas. This data could be influential when informing parents about the safety risks associated with a particular school district, helping them make informed decisions about their children’s education and well-being.

LS0tCnRpdGxlOiAiV2VlayA3IC0gR2VvIFNwYWNpYWwgRGF0YSBWaXN1YWxpemF0aW9uIgphdXRob3I6ICJKYWNvYiBNYXJ0aW4iCmRhdGU6ICJXZXN0IENoZXN0ZXIgVW5pdmVyc2l0eSAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCiAgICBmaWdfd2lkdGg6IDYKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIGZpZ19oZWlnaHQ6IDQKLS0tCgpgYGB7PWh0bWx9CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgpkaXYjVE9DIGxpIHsKICAgIGxpc3Qtc3R5bGU6bm9uZTsKICAgIGJhY2tncm91bmQtY29sb3I6bGlnaHRncmF5OwogICAgYmFja2dyb3VuZC1pbWFnZTpub25lOwogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsKICAgIGJhY2tncm91bmQtcG9zaXRpb246MDsKICAgIGZvbnQtZmFtaWx5OiBBcmlhbCwgSGVsdmV0aWNhLCBzYW5zLXNlcmlmOwogICAgY29sb3I6ICM3ODBjMGM7Cn0KCi8qIG1vdXNlIG92ZXIgbGluayAqLwpkaXYjVE9DIGE6aG92ZXIgewogIGNvbG9yOiByZWQ7Cn0KCi8qIHVudmlzaXRlZCBsaW5rICovCmRpdiNUT0MgYTpsaW5rIHsKICBjb2xvcjogYmx1ZTsKfQoKCgpoMS50aXRsZSB7CiAgZm9udC1zaXplOiAyNHB4OwogIGNvbG9yOiBEYXJrYmx1ZTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC1mYW1pbHk6IEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7CiAgZm9udC12YXJpYW50LWNhcHM6IG5vcm1hbDsKfQpoNC5hdXRob3IgeyAKICAgIGZvbnQtc2l6ZTogMThweDsKICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDQuZGF0ZSB7IAogIGZvbnQtc2l6ZTogMThweDsKICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CmgxIHsKICAgIGZvbnQtc2l6ZTogMjRweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDIgewogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmgzIHsgCiAgICBmb250LXNpemU6IDE1cHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCi8qIHVudmlzaXRlZCBsaW5rICovCmE6bGluayB7CiAgY29sb3I6IGdyZWVuOwp9CgovKiB2aXNpdGVkIGxpbmsgKi8KYTp2aXNpdGVkIHsKICBjb2xvcjogZ3JlZW47Cn0KCi8qIG1vdXNlIG92ZXIgbGluayAqLwphOmhvdmVyIHsKICBjb2xvcjogcmVkOwp9CgovKiBzZWxlY3RlZCBsaW5rICovCmE6YWN0aXZlIHsKICBjb2xvcjogeWVsbG93Owp9Cgo8L3N0eWxlPgpgYGAKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCAKIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuCm9wdGlvbnMocmVwb3MgPSBsaXN0KENSQU49Imh0dHA6Ly9jcmFuLnJzdHVkaW8uY29tLyIpKQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCiAgIGxpYnJhcnkodGlkeXZlcnNlKQp9CmlmICghcmVxdWlyZSgia25pdHIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpCiAgIGxpYnJhcnkoa25pdHIpCn0KaWYgKCFyZXF1aXJlKCJjb3dwbG90IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiY293cGxvdCIpCiAgIGxpYnJhcnkoY293cGxvdCkKfQppZiAoIXJlcXVpcmUoImxhdGV4MmV4cCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImxhdGV4MmV4cCIpCiAgIGxpYnJhcnkobGF0ZXgyZXhwKQp9CmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikKICAgbGlicmFyeShwbG90bHkpCn0KaWYgKCFyZXF1aXJlKCJnYXBtaW5kZXIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJnYXBtaW5kZXIiKQogICBsaWJyYXJ5KGdhcG1pbmRlcikKfQppZiAoIXJlcXVpcmUoInBuZyIpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbmciKSAgICAgICAgICAgICAjIEluc3RhbGwgcG5nIHBhY2thZ2UKICAgIGxpYnJhcnkoInBuZyIpCn0KaWYgKCFyZXF1aXJlKCJSQ3VybCIpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKCJSQ3VybCIpICAgICAgICAgICAjIEluc3RhbGwgUkN1cmwgcGFja2FnZQogICAgbGlicmFyeSgiUkN1cmwiKQp9CmlmICghcmVxdWlyZSgiY29sb3VycGlja2VyIikpIHsKICAgIGluc3RhbGwucGFja2FnZXMoImNvbG91cnBpY2tlciIpICAgICAgICAgICAgICAKICAgIGxpYnJhcnkoImNvbG91cnBpY2tlciIpCn0KaWYgKCFyZXF1aXJlKCJnaWZza2kiKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2lmc2tpIikgICAgICAgICAgICAgIAogICAgbGlicmFyeSgiZ2lmc2tpIikKfQppZiAoIXJlcXVpcmUoIm1hZ2ljayIpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKCJtYWdpY2siKSAgICAgICAgICAgICAgCiAgICBsaWJyYXJ5KCJtYWdpY2siKQp9CmlmICghcmVxdWlyZSgiZ3JEZXZpY2VzIikpIHsKICAgIGluc3RhbGwucGFja2FnZXMoImdyRGV2aWNlcyIpICAgICAgICAgICAgICAKICAgIGxpYnJhcnkoImdyRGV2aWNlcyIpCn0KIyMjIGdncGxvdCBhbmQgZXh0ZW5zaW9ucwppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpICAgICAgICAgICAgICAKICAgIGxpYnJhcnkoImdncGxvdDIiKQp9CmlmICghcmVxdWlyZSgiZ2dhbmltYXRlIikpIHsKICAgIGluc3RhbGwucGFja2FnZXMoImdnYW5pbWF0ZSIpICAgICAgICAgICAgICAKICAgIGxpYnJhcnkoImdnYW5pbWF0ZSIpCn0KaWYgKCFyZXF1aXJlKCJnZ3JpZGdlcyIpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JpZGdlcyIpICAgICAgICAgICAgICAKICAgIGxpYnJhcnkoImdncmlkZ2VzIikKfQppZiAoIXJlcXVpcmUoImdyYXBoaWNzIikpIHsKICAgIGluc3RhbGwucGFja2FnZXMoImdyYXBoaWNzIikgICAgICAgICAgICAgIAogICAgbGlicmFyeSgiZ3JhcGhpY3MiKQp9CmlmICghcmVxdWlyZSgidGlkeXIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5ciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgIGxpYnJhcnkodGlkeXIpCn0KaWYgKCFyZXF1aXJlKCJyZXNoYXBlMiIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInJlc2hhcGUyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgbGlicmFyeShyZXNoYXBlMikKfQppZiAoIXJlcXVpcmUoImxlYWZsZXQiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJsZWFmbGV0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgbGlicmFyeShsZWFmbGV0KQp9CmlmICghcmVxdWlyZSgiaHRtbHRvb2xzIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiaHRtbHRvb2xzIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgbGlicmFyeShodG1sdG9vbHMpCn0KCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSBUUlVFLCAgIAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BKQpgYGAKCgojIyBPdmVydmlldwpJbiB0aGlzIHdlZWtzIGFzc2lnbm1lbnQgd2Ugd2lsbCBzaG93IGhvdyB0byBjcmVhdGUgbWFwcyB3aGljaCBjb250YWluIGdlbyBzcGFjaWFsIGluZm9ybWF0aW9uLiBGaXJzdCwgd2Ugd2lsbCBidWlsZCBhIHNjYXR0ZXIgbWFwIHdoaWNoIHNob3dzIGluZm9ybWF0aW9uIGFib3V0IHZhcmlvdXMgZ2FzIHN0YXRpb25zIGluIHRoZSBVbml0ZWQgU3RhdGVzLiBBZnRlciB0aGF0LCB3ZSB3aWxsIG5hcnJvdyBkb3duIG91ciB2aWV3IHRvIHRoZSBjaXR5IG9mIFBoaWxhZGVscGhpYTsgYW5hbHl6aW5nIGNyaW1lIGRhdGEgZnJvbSB0aGUgeWVhcnMgb2YgMjAxNC0yMDE1LiBMZXQncyBzZWUgd2hhdCBraW5kIG9mIHN0b3J5IHdlIGNhbiBleHRyYWN0IGJ5IHZpc3VhbGl6aW5nIG91ciBkYXRhIHNldHMuCgojIyBHYXMgU3RhdGlvbiBTb3VyY2UgRGF0YQpJdCdzIGltcG9ydGFudCB0byBkZXNjcmliZSBhbnkgZGF0YSBzZXQgYmVmb3JlIHdvcmtpbmcgd2l0aCBpdCwgc28gbGV0J3MgYmVnaW4gYnkgYW5hbHl6aW5nIHRoZSByYXcgZ2FzIHN0YXRpb24gZGF0YS4gCkluIHRoZSBwcmV2aW91cyB3ZWVrcyBhc3NpZ25lbWVudHMsIHdlIGhhdmUgbG9hZGVkIG91ciBkYXRhIHZpYSBoYXZpbmcgdGhlIGNzdiBpbiBhIGxvY2FsIHdvcmtpbmcgZGlyZWN0b3J5LiBGb3IgdGhpcyB3ZWVrLCBkYXRhIHdhcyB1cGxvYWRlZCB0byBteSBnaXRodWIuIEhlcmUgaXMgaG93IHdlIGNhbiBsb2FkIHRoZSBkYXRhIHZpYSBodHRwcy4gCgpgYGB7ciBlY2hvPVRSVUV9Cmdhc19kYXRhIDwtIHJlYWQuY3N2KCJodHRwczovL2ptYXJ0aW4xMi5naXRodWIuaW8vU1RBVDU1My9kYXRhL2dhc19zdGF0aW9uX2RhdGEuY3N2IiwgaGVhZGVyID0gVFJVRSkKYGBgCgpTaW1wbGUgYXMgdGhhdCEgRmlyc3QsIGxldCdzIHZpZXcgdGhlIHJhdyBkYXRhIGluIGl0J3MgdGFibGUgZm9ybWF0LCBhbG9uZyB3aXRoIHNlZWluZyBob3cgbWFueSByb3dzIHdlIGhhdmUuIAoKYGBge3IgZWNobz1GQUxTRX0KaGVhZChnYXNfZGF0YSwgMikKbnJvdyhnYXNfZGF0YSkKYGBgCgpXaGlsZSB0aGVyZSBhcmUgbWFueSBjb2x1bW5zIGluIHRoaXMgZGF0YSBzZXQsIHdoYXQncyBpbXBvcnRhbnQgdG8gbm90aWNlIGZvciB0aGlzIHdlZWtzIGxlc3NvbiBpcyB0aGF0IHdlIGhhdmUgdHdvIGNvbHVtbnMgdGl0bGVkIGFzIGB5Y29vcmRgIGFuZCBgeGNvb3JkYC4gVGhlc2UgY29sdW1ucyByZXByZXNlbnQgdGhlIGNvb3JkaW5hdGVzIGluIGBsYXRpdHVkZWAgYW5kIGBsb25naXR1ZGVgLCByZXNwZWN0aXZlbHkuIFdlIHdpbGwgdXNlIGJvdGggb2YgdGhlc2UgY29sdW1ucyBsYXRlciB3aGVuIGNyZWF0aW5nIG91ciBtYXAuCgpJdCdzIGFsc28gaW1wb3J0YW50IHRvIG5vdGUgdGhlIHNpemUgb2YgdGhlIGRhdGEgc2V0OyBhIG1lcmUgNzBLIHJvd3MhIElmIHdlIHBsb3R0ZWQgYWxsIG9mIHRoZXNlIG9uIG91ciBtYXAsIGl0IHdvdWxkIGJlIGNsdXR0ZXJlZCBhbmQgZGlmZmljdWx0IHRvIGludGVycHJldC4gTGV0J3MgcmVkdWNlIG91ciBkYXRhIHNldCBzaXplIHRvIHNpbXBseSA1MDAgcmFuZG9tIHJvd3MuIFRoZSBjb2RlIGJlbG93IGRlbW9uc3RyYXRlcyBob3cgd2UgZG8gdGhpczoKCmBgYHtyIGVjaG8gPSBUUlVFfQojIFNldCB0aGUgc2VlZCBzbyB5b3UgZ2V0IHRoZSBzYW1lIHJvd3MgSSBnb3QuCnNldC5zZWVkKDEyMykKCiMgR2V0IHRoZSA1MDAgcmFuZG9tIHJvd3MKcmVkdWNlZF9nYXNfZGF0YSA8LSBnYXNfZGF0YVtzYW1wbGUobnJvdyhnYXNfZGF0YSksIDUwMCksIF0KCiMgUHJpbnQgdGhlIHJvdyBjb3VudApwcmludChucm93KHJlZHVjZWRfZ2FzX2RhdGEpKQpgYGAKClN1Y2Nlc3MhIFdlIGhhdmUgcmVkdWNlZCBvdXIgZGF0YSB0byA1MDAgcmFuZG9tIHJvd3MuCgojIyBDcmVhdGluZyBhIEJhc2ljIEdlbyBTcGFjaWFsIEluZm9ybWF0aXZlIFNjYXR0ZXIgTWFwCkZvciB0aGlzIHdlZWssIHdlIHdpbGwgZm9jdXMgb24gY3JlYXRpbmcgbWFwcyB1c2luZyB0aGUgYGxlYWZsZXRgIGxpYnJhcnkuIFBsZWFzZSB2aWV3IHRoZSBnZW5lcmF0ZWQgbWFwLCB3aGlsZSByZWFkaW5nIGFuIGV4cGxhbmF0aW9uIG9mIHRoZSBjb2RlIGJlbG93LiAKCmBgYHtyIGVjaG89VFJVRX0KIyBDcmVhdGUgYSBsZWFmbGV0IG1hcApnYXNfbWFwIDwtIGxlYWZsZXQoKSAlPiUKICBhZGRUaWxlcygpICU+JQogIHNldFZpZXcobG5nID0gLTk4LjU3OTUsIGxhdCA9IDM5LjgyODMsIHpvb20gPSAzKQoKIyBBZGQgbWFya2VycyBmb3IgZWFjaCBnYXMgc3RhdGlvbgpmb3IgKGkgaW4gMTpucm93KHJlZHVjZWRfZ2FzX2RhdGEpKSB7CiAgZ2FzX21ldGEgPSBwYXN0ZSgnc3RhdGU6ICcscmVkdWNlZF9nYXNfZGF0YSRTVEFURVtpXSwgJ1xuIGNvdW50eTogJywgcmVkdWNlZF9nYXNfZGF0YSRjb3VudHlbaV0sICdcbiBjaXR5OiAnICxyZWR1Y2VkX2dhc19kYXRhJENJVFlbaV0gLCdcbiBhZGRyZXNzOiAnLCByZWR1Y2VkX2dhc19kYXRhJEFERFJFU1NbaV0pCiAgCiAgZ2FzX21hcCA8LSBhZGRNYXJrZXJzKAogICAgbWFwID0gZ2FzX21hcCwKICAgIGxuZyA9IHJlZHVjZWRfZ2FzX2RhdGEkeGNvb3JkW2ldLAogICAgbGF0ID0gcmVkdWNlZF9nYXNfZGF0YSR5Y29vcmRbaV0sCiAgICBsYWJlbCA9IGh0bWx0b29sczo6SFRNTChodG1sRXNjYXBlKGdhc19tZXRhKSkKICAgICkKfQoKZ2FzX21hcApgYGAKCldhbGtpbmcgdGhyb3VnaCB0aGUgY29kZSwgd2UgZmlyc3Qgc3RhcnQgb3V0IHdpdGggYGdhc19tYXAgPC0gbGVhZmxldCgpICU+JSBhZGRUaWxlcygpICU+JSBzZXRWaWV3KGxuZyA9IC05OC41Nzk1LCBsYXQgPSAzOS44MjgzLCB6b29tID0gMylgLgpcClRoZSBvbmUgdGhpbmcgdGhhdCBzaG91bGQgYmUgbm90ZWQgaXMgdGhhdCB0aGUgZGVmYXVsdCB2aWV3IHdhcyB0YWtlbiBhcyB0aGUgY2VudGVyIHBvaW50IG9mIHRoZSBVU0EuIFNpbmNlIG91ciByZWR1Y2VkIGRhdGEgd2FzIHJhbmRvbWx5IHRha2VuIGZyb20gdGhlIG9yaWdpbmFsIDcwSyByb3dzLCB1c2luZyB0aGUgbWVhbiBvZiB0aGUgbG5nIGFuZCBsYXQgdmFsdWVzIHdpbGwgbm90IGdpdmUgdXMgYSBjb25zaXN0ZW50bHkgZ29vZCB2aWV3LCBiZWNhdXNlIHRoZSB2aWV3IGlzIHRoZW4gc3ViamVjdCB0byB3aGF0IGdhcyBzdGF0aW9ucyB3ZSBoYXZlIHJhbmRvbWx5IGNob3Nlbi4gVG8gY2lyY3VtdmVudCB0aGlzLCB0aGUgY2VudGVyIHBvaW50IG9mIHRoZSBVU0Egd2FzIHVzZWQuIAoKQXNpZGUgdG8gdGhhdCwgd2UgdGhlbiB1c2UgYSBiYXNpYyBgZm9yYCBsb29wIGNvbnRyb2wgc3RydWN0dXJlLCBpbiBjb21iaW5hdGlvbiB3aXRoIHRoZSBgcGFzdGUoLi4uKWAgZnVuY3Rpb24gdG8gYWRkIGxvY2F0aW9uIG1ldGFkYXRhIHVzaW5nIHRoZSBgY2l0eWAsIGBzdGF0ZWAsIGBjb3VudHlgLCBhbmQgYGFkZHJlc3NgIGZyb20gb3VyIGRhdGEgc2V0LgoKVGhpcyBpcyBhIHByaW1pdGl2ZSBleGFtcGxlIG9mIGhvdyB0aGUgYGxlYWZsZXRgIGxpYnJhcnkgY2FuIGJlIHVzZWQgdG8gZ3JhcGggZ2VvZ3JhcGhpY2FsIGluZm9ybWF0aW9uLiBMZXQncyBtb3ZlIG9udG8gYSBtb3JlIGFkdmFuY2VkIGV4YW1wbGUgc28gdGhhdCB3ZSBhcmUgYWJsZSB0byBleHRyYWN0IG1vcmUgdXNlZnVsIGluZm9ybWF0aW9uIGFuZCBkZWNpcGhlciBhIHN0b3J5IHRoZXJlaW4uCgojIyBQaGlsbHkgQ3JpbWUgRGF0YQoKRmlyc3QsIHdlIHdpbGwgbmVlZCBhIG5ldyBkYXRhIHNldCB0aGF0IGlzIGhvc3RlZCBvbiBteSBnaXRodWIuIAoKYGBge3IgZWNobz1UUlVFfQpwaGlsbHlfZGF0YSA8LSByZWFkLmNzdigiaHR0cHM6Ly9qbWFydGluMTIuZ2l0aHViLmlvL1NUQVQ1NTMvZGF0YS9QaGlsbHlDcmltZVNpbmNlMjAxNS5jc3YiLCBoZWFkZXIgPSBUUlVFKQpgYGAKCkFuIGV4YW1wbGUgZmV3IHJvd3MgYXJlIGdpdmVuOiAKCmBgYHtyIGVjaG89RkFMU0V9CmhlYWQocGhpbGx5X2RhdGEsIDIpCmBgYAoKVGhlIHRvdGFsIGFtb3VudCBvZiByb3dzIGFyZTogCgpgYGB7ciBlY2hvPUZBTFNFfQpucm93KHBoaWxseV9kYXRhKQpgYGAKClBoaWxseSBpcyBkZWZpbml0ZWx5IGtub3duIGZvciBjcmltZSBpbiBjZXJ0YWluIGFyZWFzLiBMZXQncyBmb2N1cyBpbiBvbiB0aGUgeWVhciAyMDIzLiBUbyBhY2NvbXBsaXNoIHRoaXMsIHdlIHdpbGwgaGF2ZSB0byBwZXJmb3JtIHNvbWUgZGF0YSB0cmFuc2Zvcm1hdGlvbnMgYWdhaW5zdCB0aGUgb3JpZ2luYWwgYGRhdGVgIGNvbHVtbi4gRmlyc3QsIHRoZSBmb2xsb3dpbmcgY29kZSBwYXJzZXMgb3V0IHRoZSBgeWVhcmAgcG9ydGlvbiwgYW5kIHN0b3JlcyBpdCBpbiBhIHZlY3Rvci4KCgpgYGB7ciBlY2hvPVRSVUV9CnllYXJzIDwtIGNoYXJhY3Rlcihucm93KHBoaWxseV9kYXRhKSkKCmZvciAoaSBpbiAxOm5yb3cocGhpbGx5X2RhdGEpKSB7CiAgZGF0ZV9zdHJpbmcgPC0gcGhpbGx5X2RhdGEkZGF0ZVtpXQogIGRhdGVfb2JqZWN0IDwtIHN0cnB0aW1lKGRhdGVfc3RyaW5nLCAiJW0vJWQvJVkgJUg6JU0iKQogIHllYXJzW2ldIDwtIGZvcm1hdChkYXRlX29iamVjdCwgIiVZIikKfQoKaGVhZCh5ZWFycykKYGBgCgpHcmVhdCEgTm93IHdlIGhhdmUgYWxsIHRoZSB5ZWFycy4gTGV0J3MgYWRkIGl0IGFzIGEgbmV3IGNvbHVtbiB0byBvdXIgb3JpZ2luYWwgcGhpbGx5IGNyaW1lIGRhdGFzZXQuCgpgYGB7ciBlY2hvPVRSVUV9CnBoaWxseV9kYXRhJHllYXIgPC0geWVhcnMKaGVhZChwaGlsbHlfZGF0YSwgMikKYGBgCgpFYXN5IGFzIHRoYXQhIFdlIGNhbiBzZWUgdGhlIHllYXIgY29sdW1uIGlzIG5vdyBhZGRlZCB0byB0aGUgZGF0YSBzZXQuIApUbyBuYXJyb3cgZG93biBhIHN1YnNldCBvZiAyMDIzIGRhdGEgb25seSwgd2UgY2FuIHJlZHVjZSBvdXIgZGF0YSBzZXQgYnkgZmlsdGVyaW5nIG9uIHRoZSBuZXdseSBhZGRlZCBgeWVhcmAgY29sdW1uLgoKYGBge3IgZWNobz1UUlVFfQpwaGlsbHlfZGF0YV8yMDIzIDwtIHN1YnNldChwaGlsbHlfZGF0YSwgeWVhciA9PSAyMDIzKQpoZWFkKHBoaWxseV9kYXRhXzIwMjMkZGF0ZSwgNSkKYGBgCgpPbmx5IHRoZSBgZGF0ZWAgY29sdW1uIGlzIHNob3duIGluIHRoZSBhYm92ZSBleGFtcGxlIC0tIHRoZSByZW1haW5pbmcgY29sdW1ucyBhcmUgc3RpbGwgc3RvcmVkIGluIG1lbW9yeS4gV2UgY2FuIGNsZWFybHkgc2VlIHRoZXJlIGFyZSBvbmx5IGRhdGVzIGluIGAyMDIzYC4gClwKCiMjIFBoaWxseSBDcmltZXMgLSBNYXBwZWQKTm93IHRoYXQgd2UgaGF2ZSBvdXIgMjAyMyBkYXRhLCBsZXQncyBnbyBhaGVhZCBhbmQgdXNlIGxlYWZseSB0byBtYXAgb3VyIGRhdGEuIFRoZSBjb2RlIGJlbG93IGFkZHMgdHdvIGFkZGl0aW9uYWwgY29sdW1ucyB0byB0aGUgZGF0YXNldCBzbyB3ZSBjYW4gZWFzaWx5IGRldGVybWluZSB3aGF0IGNvbG9ycyB0byB1c2UsIGFuZCBpbiBhZGRpdGlvbiBnZW5lcmF0aW5nIHRoZSBsYWJlbCBpbmZvcm1hdGlvbi4gCgpGcm9tIHRoZXJlLCBhIHRpdGxlIGFsb25nIHdpdGggYSBsZWdlbmQgaXMgYWRkZWQgdG8gdGhlIGdyYXBoLiAKCmBgYHtyIGVjaG89VFJVRX0KY29sb3JzIDwtIGNoYXJhY3Rlcihucm93KHBoaWxseV9kYXRhXzIwMjMpKQpsYWJlbHMgPC0gY2hhcmFjdGVyKG5yb3cocGhpbGx5X2RhdGFfMjAyMykpCiAKZm9yIChpIGluIDE6bnJvdyhwaGlsbHlfZGF0YV8yMDIzKSkgewogICMgSGFuZGxlIHRoZSBjb2xvcnMKICBpZiAocGhpbGx5X2RhdGFfMjAyMyRmYXRhbFtpXSA9PSAnRmF0YWwnKSB7CiAgICBjb2xvcnNbaV0gPC0gJyMwMDAwMDAnCiAgfQogIGVsc2UgewogICAgY29sb3JzW2ldIDwtICcjQ0M3OUE3JyAgICAKICB9CiAgCiAgIyBIYW5kbGUgdGhlIGluZm8gdG8gZGlzcGxheSBpbiB0aGUgbGFiZWwKICBsYWJlbHNbaV0gPC0gcGFzdGUoJ0dlbmRlcjogJywgcGhpbGx5X2RhdGFfMjAyMyRzZXhbaV0sICdOZWlnaGJvcmhvb2Q6ICcsIHBoaWxseV9kYXRhXzIwMjMkbmVpZ2hib3Job29kW2ldLCAnQmxvY2sgTnVtYmVyOiAnLCBwaGlsbHlfZGF0YV8yMDIzJGJsb2NrX251bWJlcltpXSkKfQoKcGhpbGx5X2RhdGFfMjAyMyRjcmltZV90eXBlX2NvbG9yIDwtIGNvbG9ycwpwaGlsbHlfZGF0YV8yMDIzJGxhYmVsIDwtIGxhYmVscwoKIyMgTWFwIHRpdGxlCnRpdGxlIDwtIHRhZ3MkZGl2KCBIVE1MKCc8Zm9udCBjb2xvciA9ICJkYXJrcmVkIiBzaXplID00PjxiPlBoaWxseSBGYXRhbCBhbmQgTm9uLUZhdGFsIENyaW1lcyAyMDIzPC9iPjwvZm9udD4nKQopCgojIENyZWF0ZSBhIExlYWZsZXQgbWFwCm0gPC0gbGVhZmxldChwaGlsbHlfZGF0YV8yMDIzKSAlPiUKICBhZGRUaWxlcygpICU+JQogIHNldFZpZXcobGF0ID0gNDAsIGxuZyA9IC03NS4xNjUyLCB6b29tID0gMTEpICU+JQogIGFkZENpcmNsZU1hcmtlcnMoCiAgICBsbmcgPSB+bG5nLAogICAgbGF0ID0gfmxhdCwKICAgIGNvbG9yID0gfmNyaW1lX3R5cGVfY29sb3IsCiAgICByYWRpdXMgPSAzLAogICAgbGFiZWwgPSB+bGFiZWwsCiAgICBsYWJlbE9wdGlvbnMgPSBsYWJlbE9wdGlvbnMobm9IaWRlID0gRkFMU0UsIHRleHRPbmx5ID0gRkFMU0UpKSAlPiUKICBhZGRDb250cm9sKHRpdGxlLCBwb3NpdGlvbiA9ICJ0b3ByaWdodCIpICU+JQogIGFkZExlZ2VuZChwb3NpdGlvbiA9ICJib3R0b21sZWZ0IiwgCiAgICAgICAgICAgIGNvbG9ycyA9IGMoIiNDQzc5QTciLCAiIzAwMDAwMCIpLAogICAgICAgICAgICBsYWJlbHM9IGMoIk5vbiBGYXRhbCIsICJGYXRhbCIpLAogICAgICAgICAgICB0aXRsZT0gIkNyaW1lIFR5cGUiLAogICAgICAgICAgICBvcGFjaXR5ID0gMC44KQoKbQpgYGAKClwKXAoKIyMgTmFycmF0aW9uCgpJdCBpcyBwYXJ0aWN1bGFybHkgaW50ZXJlc3RpbmcgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgY3JpbWVzIGFjcm9zcyB0aGUgY2l0eSBvZiBQaGlsbHkuIFRoZXJlIGFyZSBhIGZldyBob3RzcG90cyBvZiBmYXRhbCBjcmltZXMuIFRoZSBwcmltYXJ5IGhvdHNwb3QgYmVpbmcgdGhlIHNvdXRod2VzdCByZWdpb24gb2YgdGhlIGNpdHksIGZvbGxvd2VkIGJ5IGEgc21hbGxlciBob3RzcG90IGp1c3Qgbm9ydGggb2YgdGhlIGNpdHkuCgpUaGUgZGF0YXNldCBpdHNlbGYgY2FuIHRlbGwgdXMgbW9yZSB3aXRoIGRpZmZlcmVudCBncmFwaHMuIExldCdzIHZpZXcgdGhlICUgb2YgY3JpbWVzIGNvbW1pdHRlZCBieSBnZW5kZXIuCgpgYGAge3IgZWNobz1UUlVFfQojIENvdW50IHRoZSBudW1iZXIgb2YgY3JpbWVzIHBlciBnZW5kZXIKY3JpbWVzX3Blcl9nZW5kZXIgPC0gcGhpbGx5X2RhdGFfMjAyMyAlPiUKICBncm91cF9ieShzZXgpICU+JQogIHN1bW1hcmlzZShjcmltZV9jb3VudCA9IG4oKSkKCiMgQ2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIGVhY2ggZ2VuZGVyCmNyaW1lc19wZXJfZ2VuZGVyJHBlcmNlbnRhZ2UgPC0gKGNyaW1lc19wZXJfZ2VuZGVyJGNyaW1lX2NvdW50IC8gc3VtKGNyaW1lc19wZXJfZ2VuZGVyJGNyaW1lX2NvdW50KSkgKiAxMDAKCiMgQ3JlYXRlIGEgcGllIGNoYXJ0CnAzIDwtIHBsb3RfbHkoY3JpbWVzX3Blcl9nZW5kZXIsIGxhYmVscyA9IH5zZXgsIHZhbHVlcyA9IH5wZXJjZW50YWdlLCB0eXBlID0gJ3BpZScpICU+JQogIGxheW91dCh0aXRsZSA9ICIlIG9mIENyaW1lcyBDb21taXR0ZWQgYnkgTWFsZXMgdnMuIEZlbWFsZXMiKQoKIyBQcmludCB0aGUgcGxvdApwMwpgYGAKCgoKSXQgc2VlbXMgdGhhdCBtYWxlcyBjb21taXQgbW9zdCBvZiB0aGUgY3JpbWVzIGluIHRoZSBjaXR5IG9mIFBoaWxhZGVscGhpYS4gU29tZXRoaW5nIGVsc2Ugb2YgaW50ZXJlc3QgY291bGQgYmUgdG8gYW5hbHl6ZSB3aGljaCBzY2hvb2wgZGlzdHJpY3RzIGhhdmUgdGhlIGhpZ2hlc3QgY3JpbWUgY291bnQuIEluIHBhcnRpY3VsYXIsIHBhcmVudHMgY291bGQgZmluZCB0aGlzIGRhdGEgdG8gYmUgb2YgdXNlIHdoZW4gZGV0ZXJtaW5pbmcgd2hhdCBzY2hvb2wgZGlzdHJpY3QgdG8gc2VuZCB0aGVpciBraWRzIHRvLiAgIAoKYGBge3IgZWNobz1UUlVFfQogICMgQ291bnQgdGhlIG51bWJlciBvZiBjcmltZXMgcGVyIHNjaG9vbCBkaXN0cmljdAogIGNyaW1lc19wZXJfZGlzdHJpY3QgPC0gcGhpbGx5X2RhdGFfMjAyMyAlPiUKICAgIGdyb3VwX2J5KHNjaG9vbF9jYXRjaG1lbnQpICU+JQogICAgc3VtbWFyaXNlKGNyaW1lX2NvdW50ID0gbigpKSAlPiUKICAgIHRvcF9uKDEwLCBjcmltZV9jb3VudCkKCiAgCiAgIyBDcmVhdGUgYSBiYXIgZ3JhcGgKICBwIDwtIHBsb3RfbHkoY3JpbWVzX3Blcl9kaXN0cmljdCwgeSA9IH5zY2hvb2xfY2F0Y2htZW50LCB4ID0gfmNyaW1lX2NvdW50LCB0eXBlID0gJ2JhcicpICU+JQogICAgbGF5b3V0KHRpdGxlID0gIk51bWJlciBvZiBDcmltZXMgcGVyIFNjaG9vbCBEaXN0cmljdCBpbiBQaGlsYWRlbHBoaWEgKDIwMjMpIiwKICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiU2Nob29sIERpc3RyaWN0IiksCiAgICAgICAgICAgeWF4aXMgPSBsaXN0KCh0aXRsZSA9ICJOdW1iZXIgb2YgQ3JpbWVzIikpKQogIAogICMgUHJpbnQgdGhlIHBsb3QKICBwCiAgCmBgYAoKVGhlc2UgdGVuIHNjaG9vbHMgYXJlIHRoZSBzY2hvb2xzIHdpdGggdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIHJlcG9ydGVkIGNyaW1lcywgaW5kaWNhdGluZyBhIHBvdGVudGlhbGx5IGhpZ2hlciBjcmltZSByYXRlIGluIHRoZXNlIGFyZWFzLiBUaGlzIGRhdGEgY291bGQgYmUgaW5mbHVlbnRpYWwgd2hlbiBpbmZvcm1pbmcgcGFyZW50cyBhYm91dCB0aGUgc2FmZXR5IHJpc2tzIGFzc29jaWF0ZWQgd2l0aCBhIHBhcnRpY3VsYXIgc2Nob29sIGRpc3RyaWN0LCBoZWxwaW5nIHRoZW0gbWFrZSBpbmZvcm1lZCBkZWNpc2lvbnMgYWJvdXQgdGhlaXIgY2hpbGRyZW4ncyBlZHVjYXRpb24gYW5kIHdlbGwtYmVpbmcuCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg==